#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <arpa/inet.h>		//内部包含了socket.h
#include <sys/select.h>

//用于线程同步的互斥锁
pthread_mutex_t mx;

//用于线程通讯的信息结构体
typedef struct fd_info{
	int fd;				//用于监听的文件描述符
	int *max_fd;		//三个文件描述符集合中最大的文件描述符
	fd_set* r_fd_set;	//用于通讯读的文件描述符集合

	//fd_set* w_fd_set;	//用于通讯写的文件描述符集合
	//fd_set* e_fd_set;	//用于异常报错的文件描述符集合
}fd_info;

void* accept_connect(void* arg)
{
	printf("子线程ID = %ld\n", pthread_self());
	fd_info* info = (fd_info*)arg;
	//接收连接
	int cfd = accept(info->fd, NULL, NULL);

	//将连接成功后的文件描述符放回原始的集合中 (注意修改共享资源时加锁)
	pthread_mutex_lock(&mx);
	FD_SET(cfd, info->r_fd_set);
	*info->max_fd = cfd > *info->max_fd ? cfd : *info->max_fd;
	pthread_mutex_unlock(&mx);
	free(info);
	return NULL;
}

void* communication(void* arg)
{
	printf("子线程ID = %ld\n", pthread_self());
	fd_info* info = (fd_info*)arg;
	//接受数据
	char buff[1024];
	int len = recv(info->fd, buff, sizeof(buff), 0);
	if (len == 0)
	{
		printf("客户端断开连接。。。\n");
		//客户端断开连接后需要从文件描述符集合中删除对应的文件描述符
		pthread_mutex_lock(&mx);
		FD_CLR(info->fd, info->r_fd_set);
		pthread_mutex_unlock(&mx);
		close(info->fd);
		free(info);
		return NULL;
	}
	else if (len == -1)
	{
		perror("recv error");
		free(info);
		return NULL;
	}

	printf("read buff = %s\n", buff);
	//小写转大写发回给客户端
	for (int j = 0; j < len; j++)
	{
		buff[j] = toupper(buff[j]);
	}
	printf("after buff = %s\n", buff);
	int res = send(info->fd, buff, sizeof(buff), 0);
	if (res == -1)
	{
		perror("send error");
		free(info);
		return NULL;
	}
	return NULL;
}

int main()
{
	pthread_mutex_init(&mx, NULL);
	//第一步：创建监听的套接字
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd == -1)
	{
		perror("socket");
		exit(1);
	}

	//第二步：绑定本地的IP 和 port
	struct sockaddr_in saddr;
	memset(&saddr, 0, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(9999);		
	saddr.sin_addr.s_addr = htonl(INADDR_ANY);

	int res = bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));
	if (res == -1)
	{
		perror("bind");
		exit(1);
	}

	//第三步：设置监听
	res = listen(fd, 64);
	if (res == -1)
	{
		perror("listen");
		exit(1);
	}

	fd_set r_fd_set;
	FD_ZERO(&r_fd_set);
	FD_SET(fd, &r_fd_set);

	int max_fd = fd;
	while (1)
	{
		//更新文件描述符集合
		pthread_mutex_lock(&mx);
		fd_set temp = r_fd_set;
		pthread_mutex_unlock(&mx);

		int res = select(max_fd + 1, &temp, NULL, NULL, NULL);
		
		//判断是否为监听的文件描述符
		if (FD_ISSET(fd, &temp))
		{
			//创建子线程 接收客户端连接
			pthread_t tid;
			fd_info* info = (fd_info*)malloc(sizeof(fd_info));
			info->fd = fd;
			info->max_fd = &max_fd;
			info->r_fd_set = &r_fd_set;
			pthread_create(&tid, NULL, accept_connect, info);
			pthread_detach(tid);
		}
		
		//不是监听的文件描述符就是通讯的文件描述符，需要循环遍历
		for (int i = 0; i <= max_fd; i++)
		{
			//不是监听的文件描述符且在文件描述符集合中
			if (i != fd && FD_ISSET(i, &temp))
			{
				//创建子线程
				pthread_t tid;
				fd_info* info = (fd_info*)malloc(sizeof(fd_info));
				info->fd = i;
				info->r_fd_set = &r_fd_set;
				pthread_create(&tid, NULL, communication, info);
				pthread_detach(tid);

			}
		}
	}

    //记得释放内存资源
	close(fd);
	pthread_mutex_destroy(&mx);
	return 0;
}